<!doctype html>
<html>

<head>
  <meta name="generator" content="JSDoc 3.6.11">
  <meta charset="utf-8">
  <title>whatsapp-web.js 1.34.2 &raquo; Source: structures/Message.js</title>
  <link rel="stylesheet" href="https://brick.a.ssl.fastly.net/Karla:400,400i,700,700i" type="text/css">
  <link rel="stylesheet" href="https://brick.a.ssl.fastly.net/Noto+Serif:400,400i,700,700i" type="text/css">
  <link rel="stylesheet" href="https://brick.a.ssl.fastly.net/Inconsolata:500" type="text/css">
  <link href="css/baseline.css" rel="stylesheet">
</head>

<body onload="prettyPrint()">
  <nav id="jsdoc-navbar" role="navigation" class="jsdoc-navbar">
    <div id="jsdoc-navbar-container">
      <div id="jsdoc-navbar-content">
        <a href="index.html" class="jsdoc-navbar-package-name">whatsapp-web.<wbr>js 1.<wbr>34.<wbr>2</a>
      </div>
    </div>
  </nav>
  <div id="jsdoc-body-container">
    <div id="jsdoc-content">
      <div id="jsdoc-content-container">
        <div id="jsdoc-banner" role="banner">
        </div>
        <div id="jsdoc-main" role="main">
          <header class="page-header">
            <h1>Source: structures/Message.js</h1>
          </header>
          <article>
            <pre class="prettyprint linenums"><code>&#x27;use strict&#x27;;

const Base &#x3D; require(&#x27;./Base&#x27;);
const MessageMedia &#x3D; require(&#x27;./MessageMedia&#x27;);
const Location &#x3D; require(&#x27;./Location&#x27;);
const Order &#x3D; require(&#x27;./Order&#x27;);
const Payment &#x3D; require(&#x27;./Payment&#x27;);
const Reaction &#x3D; require(&#x27;./Reaction&#x27;);
const Contact &#x3D; require(&#x27;./Contact&#x27;);
const ScheduledEvent &#x3D; require(&#x27;./ScheduledEvent&#x27;); // eslint-disable-line no-unused-vars
const { MessageTypes } &#x3D; require(&#x27;../util/Constants&#x27;);

/**
 * Represents a Message on WhatsApp
 * @extends {Base}
 */
class Message extends Base {
    constructor(client, data) {
        super(client);

        if (data) this._patch(data);
    }

    _patch(data) {
        this._data &#x3D; data;
        
        /**
         * MediaKey that represents the sticker &#x27;ID&#x27;
         * @type {string}
         */
        this.mediaKey &#x3D; data.mediaKey;
        
        /**
         * ID that represents the message
         * @type {object}
         */
        this.id &#x3D; data.id;

        /**
         * ACK status for the message
         * @type {MessageAck}
         */
        this.ack &#x3D; data.ack;

        /**
         * Indicates if the message has media available for download
         * @type {boolean}
         */
        this.hasMedia &#x3D; Boolean(data.directPath);

        /**
         * Message content
         * @type {string}
         */
        this.body &#x3D; this.hasMedia ? data.caption || &#x27;&#x27; : data.body || data.pollName || data.eventName || &#x27;&#x27;;

        /**
         * Message type
         * @type {MessageTypes}
         */
        this.type &#x3D; data.type;

        /**
         * Unix timestamp for when the message was created
         * @type {number}
         */
        this.timestamp &#x3D; data.t;

        /**
         * ID for the Chat that this message was sent to, except if the message was sent by the current user.
         * @type {string}
         */
        this.from &#x3D; (typeof (data.from) &#x3D;&#x3D;&#x3D; &#x27;object&#x27; &amp;amp;&amp;amp; data.from !&#x3D;&#x3D; null) ? data.from._serialized : data.from;

        /**
         * ID for who this message is for.
         *
         * If the message is sent by the current user, it will be the Chat to which the message is being sent.
         * If the message is sent by another user, it will be the ID for the current user.
         * @type {string}
         */
        this.to &#x3D; (typeof (data.to) &#x3D;&#x3D;&#x3D; &#x27;object&#x27; &amp;amp;&amp;amp; data.to !&#x3D;&#x3D; null) ? data.to._serialized : data.to;

        /**
         * If the message was sent to a group, this field will contain the user that sent the message.
         * @type {string}
         */
        this.author &#x3D; (typeof (data.author) &#x3D;&#x3D;&#x3D; &#x27;object&#x27; &amp;amp;&amp;amp; data.author !&#x3D;&#x3D; null) ? data.author._serialized : data.author;

        /**
         * String that represents from which device type the message was sent
         * @type {string}
         */
        this.deviceType &#x3D; typeof data.id.id &#x3D;&#x3D;&#x3D; &#x27;string&#x27; &amp;amp;&amp;amp; data.id.id.length &gt; 21 ? &#x27;android&#x27; : typeof data.id.id &#x3D;&#x3D;&#x3D; &#x27;string&#x27; &amp;amp;&amp;amp; data.id.id.substring(0, 2) &#x3D;&#x3D;&#x3D; &#x27;3A&#x27; ? &#x27;ios&#x27; : &#x27;web&#x27;;
        /**
         * Indicates if the message was forwarded
         * @type {boolean}
         */
        this.isForwarded &#x3D; data.isForwarded;

        /**
         * Indicates how many times the message was forwarded.
         *
         * The maximum value is 127.
         * @type {number}
         */
        this.forwardingScore &#x3D; data.forwardingScore || 0;

        /**
         * Indicates if the message is a status update
         * @type {boolean}
         */
        this.isStatus &#x3D; data.isStatusV3 || data.id.remote &#x3D;&#x3D;&#x3D; &#x27;status@broadcast&#x27;;

        /**
         * Indicates if the message was starred
         * @type {boolean}
         */
        this.isStarred &#x3D; data.star;

        /**
         * Indicates if the message was a broadcast
         * @type {boolean}
         */
        this.broadcast &#x3D; data.broadcast;

        /**
         * Indicates if the message was sent by the current user
         * @type {boolean}
         */
        this.fromMe &#x3D; data.id.fromMe;

        /**
         * Indicates if the message was sent as a reply to another message.
         * @type {boolean}
         */
        this.hasQuotedMsg &#x3D; data.quotedMsg ? true : false;

        /**
         * Indicates whether there are reactions to the message
         * @type {boolean}
         */
        this.hasReaction &#x3D; data.hasReaction ? true : false;

        /**
         * Indicates the duration of the message in seconds
         * @type {string}
         */
        this.duration &#x3D; data.duration ? data.duration : undefined;

        /**
         * Location information contained in the message, if the message is type &quot;location&quot;
         * @type {Location}
         */
        this.location &#x3D; (() &#x3D;&gt; {
            if (data.type !&#x3D;&#x3D; MessageTypes.LOCATION) {
                return undefined;
            }
            let description;
            if (data.loc &amp;amp;&amp;amp; typeof data.loc &#x3D;&#x3D;&#x3D; &#x27;string&#x27;) {
                let splitted &#x3D; data.loc.split(&#x27;\n&#x27;);
                description &#x3D; {
                    name: splitted[0],
                    address: splitted[1],
                    url: data.clientUrl
                };
            }
            return new Location(data.lat, data.lng, description);
        })();

        /**
         * List of vCards contained in the message.
         * @type {Array&amp;lt;string&gt;}
         */
        this.vCards &#x3D; data.type &#x3D;&#x3D;&#x3D; MessageTypes.CONTACT_CARD_MULTI ? data.vcardList.map((c) &#x3D;&gt; c.vcard) : data.type &#x3D;&#x3D;&#x3D; MessageTypes.CONTACT_CARD ? [data.body] : [];

        /**
         * Group Invite Data
         * @type {object}
         */
        this.inviteV4 &#x3D; data.type &#x3D;&#x3D;&#x3D; MessageTypes.GROUP_INVITE ? {
            inviteCode: data.inviteCode,
            inviteCodeExp: data.inviteCodeExp,
            groupId: data.inviteGrp,
            groupName: data.inviteGrpName,
            fromId: typeof data.from &#x3D;&#x3D;&#x3D; &#x27;object&#x27; &amp;amp;&amp;amp; &#x27;_serialized&#x27; in data.from ? data.from._serialized : data.from,
            toId: typeof data.to &#x3D;&#x3D;&#x3D; &#x27;object&#x27; &amp;amp;&amp;amp; &#x27;_serialized&#x27; in data.to ? data.to._serialized : data.to
        } : undefined;

        /**
         * Indicates the mentions in the message body.
         * @type {string[]}
         */
        this.mentionedIds &#x3D; data.mentionedJidList || [];

        /**
         * @typedef {Object} GroupMention
         * @property {string} groupSubject The name  of the group
         * @property {string} groupJid The group ID
         */

        /**
         * Indicates whether there are group mentions in the message body
         * @type {GroupMention[]}
         */
        this.groupMentions &#x3D; data.groupMentions || [];

        /**
         * Order ID for message type ORDER
         * @type {string}
         */
        this.orderId &#x3D; data.orderId ? data.orderId : undefined;
        /**
         * Order Token for message type ORDER
         * @type {string}
         */
        this.token &#x3D; data.token ? data.token : undefined;

        /** 
         * Indicates whether the message is a Gif
         * @type {boolean}
         */
        this.isGif &#x3D; Boolean(data.isGif);

        /**
         * Indicates if the message will disappear after it expires
         * @type {boolean}
         */
        this.isEphemeral &#x3D; data.isEphemeral;

        /** Title */
        if (data.title) {
            this.title &#x3D; data.title;
        }

        /** Description */
        if (data.description) {
            this.description &#x3D; data.description;
        }

        /** Business Owner JID */
        if (data.businessOwnerJid) {
            this.businessOwnerJid &#x3D; data.businessOwnerJid;
        }

        /** Product ID */
        if (data.productId) {
            this.productId &#x3D; data.productId;
        }

        /** Last edit time */
        if (data.latestEditSenderTimestampMs) {
            this.latestEditSenderTimestampMs &#x3D; data.latestEditSenderTimestampMs;
        }

        /** Last edit message author */
        if (data.latestEditMsgKey) {
            this.latestEditMsgKey &#x3D; data.latestEditMsgKey;
        }
        
        /**
         * Protocol message key.
         * Can be used to retrieve the ID of an original message that was revoked.
         */
        if (data.protocolMessageKey) {
            this.protocolMessageKey &#x3D; data.protocolMessageKey;
        }

        /**
         * Links included in the message.
         * @type {Array&amp;lt;{link: string, isSuspicious: boolean}&gt;}
         *
         */
        this.links &#x3D; data.links;

        /** Buttons */
        if (data.dynamicReplyButtons) {
            this.dynamicReplyButtons &#x3D; data.dynamicReplyButtons;
        }

        /** Selected Button Id **/
        if (data.selectedButtonId) {
            this.selectedButtonId &#x3D; data.selectedButtonId;
        }

        /** Selected List row Id **/
        if (data.listResponse &amp;amp;&amp;amp; data.listResponse.singleSelectReply.selectedRowId) {
            this.selectedRowId &#x3D; data.listResponse.singleSelectReply.selectedRowId;
        }

        if (this.type &#x3D;&#x3D;&#x3D; MessageTypes.POLL_CREATION) {
            this.pollName &#x3D; data.pollName;
            this.pollOptions &#x3D; data.pollOptions;
            this.allowMultipleAnswers &#x3D; Boolean(!data.pollSelectableOptionsCount);
            this.pollInvalidated &#x3D; data.pollInvalidated;
            this.isSentCagPollCreation &#x3D; data.isSentCagPollCreation;
            this.messageSecret &#x3D; data.messageSecret ? Object.keys(data.messageSecret).map((key) &#x3D;&gt; data.messageSecret[key]) : [];
        }

        return super._patch(data);
    }

    _getChatId() {
        return this.fromMe ? this.to : this.from;
    }

    /**
     * Reloads this Message object&#x27;s data in-place with the latest values from WhatsApp Web. 
     * Note that the Message must still be in the web app cache for this to work, otherwise will return null.
     * @returns {Promise&amp;lt;Message&gt;}
     */
    async reload() {
        const newData &#x3D; await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            if (!msg) return null;
            return window.WWebJS.getMessageModel(msg);
        }, this.id._serialized);

        if(!newData) return null;
        
        this._patch(newData);
        return this;
    }

    /**
     * Returns message in a raw format
     * @type {Object}
     */
    get rawData() {
        return this._data;
    }
    
    /**
     * Returns the Chat this message was sent in
     * @returns {Promise&amp;lt;Chat&gt;}
     */
    getChat() {
        return this.client.getChatById(this._getChatId());
    }

    /**
     * Returns the Contact this message was sent from
     * @returns {Promise&amp;lt;Contact&gt;}
     */
    getContact() {
        return this.client.getContactById(this.author || this.from);
    }

    /**
     * Returns the Contacts mentioned in this message
     * @returns {Promise&amp;lt;Array&amp;lt;Contact&gt;&gt;}
     */
    async getMentions() {
        return await Promise.all(this.mentionedIds.map(async m &#x3D;&gt; await this.client.getContactById(m)));
    }
    
    /**
     * Returns groups mentioned in this message
     * @returns {Promise&amp;lt;GroupChat[]|[]&gt;}
     */
    async getGroupMentions() {
        return await Promise.all(this.groupMentions.map(async (m) &#x3D;&gt; await this.client.getChatById(m.groupJid._serialized)));
    }

    /**
     * Returns the quoted message, if any
     * @returns {Promise&amp;lt;Message&gt;}
     */
    async getQuotedMessage() {
        if (!this.hasQuotedMsg) return undefined;

        const quotedMsg &#x3D; await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            const quotedMsg &#x3D; window.Store.QuotedMsg.getQuotedMsgObj(msg);
            return window.WWebJS.getMessageModel(quotedMsg);
        }, this.id._serialized);

        return new Message(this.client, quotedMsg);
    }

    /**
     * Sends a message as a reply to this message. If chatId is specified, it will be sent
     * through the specified Chat. If not, it will send the message
     * in the same Chat as the original message was sent.
     *
     * @param {string|MessageMedia|Location} content
     * @param {string} [chatId]
     * @param {MessageSendOptions} [options]
     * @returns {Promise&amp;lt;Message&gt;}
     */
    async reply(content, chatId, options &#x3D; {}) {
        if (!chatId) {
            chatId &#x3D; this._getChatId();
        }

        options &#x3D; {
            ...options,
            quotedMessageId: this.id._serialized
        };

        return this.client.sendMessage(chatId, content, options);
    }

    /**
     * React to this message with an emoji
     * @param {string} reaction - Emoji to react with. Send an empty string to remove the reaction.
     * @return {Promise}
     */
    async react(reaction){
        await this.client.pupPage.evaluate(async (messageId, reaction) &#x3D;&gt; {
            if (!messageId) return null;
            const msg &#x3D;
                window.Store.Msg.get(messageId) || (await window.Store.Msg.getMessagesById([messageId]))?.messages?.[0];
            if(!msg) return null;
            await window.Store.sendReactionToMsg(msg, reaction);
        }, this.id._serialized, reaction);
    }

    /**
     * Accept Group V4 Invite
     * @returns {Promise&amp;lt;Object&gt;}
     */
    async acceptGroupV4Invite() {
        return await this.client.acceptGroupV4Invite(this.inviteV4);
    }

    /**
     * Forwards this message to another chat (that you chatted before, otherwise it will fail)
     *
     * @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded
     * @returns {Promise}
     */
    async forward(chat) {
        const chatId &#x3D; typeof chat &#x3D;&#x3D;&#x3D; &#x27;string&#x27; ? chat : chat.id._serialized;

        await this.client.pupPage.evaluate(async (msgId, chatId) &#x3D;&gt; {
            return window.WWebJS.forwardMessage(chatId, msgId);
        }, this.id._serialized, chatId);
    }

    /**
     * Downloads and returns the attatched message media
     * @returns {Promise&amp;lt;MessageMedia&gt;}
     */
    async downloadMedia() {
        if (!this.hasMedia) {
            return undefined;
        }

        const result &#x3D; await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            if (!msg || !msg.mediaData) {
                return null;
            }
            if (msg.mediaData.mediaStage !&#x3D; &#x27;RESOLVED&#x27;) {
                // try to resolve media
                await msg.downloadMedia({
                    downloadEvenIfExpensive: true,
                    rmrReason: 1
                });
            }

            if (msg.mediaData.mediaStage.includes(&#x27;ERROR&#x27;) || msg.mediaData.mediaStage &#x3D;&#x3D;&#x3D; &#x27;FETCHING&#x27;) {
                // media could not be downloaded
                return undefined;
            }

            try {
                const decryptedMedia &#x3D; await window.Store.DownloadManager.downloadAndMaybeDecrypt({
                    directPath: msg.directPath,
                    encFilehash: msg.encFilehash,
                    filehash: msg.filehash,
                    mediaKey: msg.mediaKey,
                    mediaKeyTimestamp: msg.mediaKeyTimestamp,
                    type: msg.type,
                    signal: (new AbortController).signal
                });

                const data &#x3D; await window.WWebJS.arrayBufferToBase64Async(decryptedMedia);

                return {
                    data,
                    mimetype: msg.mimetype,
                    filename: msg.filename,
                    filesize: msg.size
                };
            } catch (e) {
                if(e.status &amp;amp;&amp;amp; e.status &#x3D;&#x3D;&#x3D; 404) return undefined;
                throw e;
            }
        }, this.id._serialized);

        if (!result) return undefined;
        return new MessageMedia(result.mimetype, result.data, result.filename, result.filesize);
    }

    /**
     * Deletes a message from the chat
     * @param {?boolean} everyone If true and the message is sent by the current user or the user is an admin, will delete it for everyone in the chat.
     * @param {?boolean} [clearMedia &#x3D; true] If true, any associated media will also be deleted from a device.
     */
    async delete(everyone, clearMedia &#x3D; true) {
        await this.client.pupPage.evaluate(async (msgId, everyone, clearMedia) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            const chat &#x3D; window.Store.Chat.get(msg.id.remote) || (await window.Store.Chat.find(msg.id.remote));
            
            const canRevoke &#x3D;
                window.Store.MsgActionChecks.canSenderRevokeMsg(msg) || window.Store.MsgActionChecks.canAdminRevokeMsg(msg);

            if (everyone &amp;amp;&amp;amp; canRevoke) {
                return window.compareWwebVersions(window.Debug.VERSION, &#x27;&gt;&#x3D;&#x27;, &#x27;2.3000.0&#x27;)
                    ? window.Store.Cmd.sendRevokeMsgs(chat, { list: [msg], type: &#x27;message&#x27; }, { clearMedia: clearMedia })
                    : window.Store.Cmd.sendRevokeMsgs(chat, [msg], { clearMedia: true, type: msg.id.fromMe ? &#x27;Sender&#x27; : &#x27;Admin&#x27; });
            }

            return window.compareWwebVersions(window.Debug.VERSION, &#x27;&gt;&#x3D;&#x27;, &#x27;2.3000.0&#x27;)
                ? window.Store.Cmd.sendDeleteMsgs(chat, { list: [msg], type: &#x27;message&#x27; }, clearMedia)
                : window.Store.Cmd.sendDeleteMsgs(chat, [msg], clearMedia);
        }, this.id._serialized, everyone, clearMedia);
    }

    /**
     * Stars this message
     */
    async star() {
        await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            if (window.Store.MsgActionChecks.canStarMsg(msg)) {
                let chat &#x3D; await window.Store.Chat.find(msg.id.remote);
                return window.Store.Cmd.sendStarMsgs(chat, [msg], false);
            }
        }, this.id._serialized);
    }

    /**
     * Unstars this message
     */
    async unstar() {
        await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            if (window.Store.MsgActionChecks.canStarMsg(msg)) {
                let chat &#x3D; await window.Store.Chat.find(msg.id.remote);
                return window.Store.Cmd.sendUnstarMsgs(chat, [msg], false);
            }
        }, this.id._serialized);
    }

    /**
     * Pins the message (group admins can pin messages of all group members)
     * @param {number} duration The duration in seconds the message will be pinned in a chat
     * @returns {Promise&amp;lt;boolean&gt;} Returns true if the operation completed successfully, false otherwise
     */
    async pin(duration) {
        return await this.client.pupPage.evaluate(async (msgId, duration) &#x3D;&gt; {
            return await window.WWebJS.pinUnpinMsgAction(msgId, 1, duration);
        }, this.id._serialized, duration);
    }

    /**
     * Unpins the message (group admins can unpin messages of all group members)
     * @returns {Promise&amp;lt;boolean&gt;} Returns true if the operation completed successfully, false otherwise
     */
    async unpin() {
        return await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            return await window.WWebJS.pinUnpinMsgAction(msgId, 2);
        }, this.id._serialized);
    }

    /**
     * Message Info
     * @typedef {Object} MessageInfo
     * @property {Array&amp;lt;{id: ContactId, t: number}&gt;} delivery Contacts to which the message has been delivered to
     * @property {number} deliveryRemaining Amount of people to whom the message has not been delivered to
     * @property {Array&amp;lt;{id: ContactId, t: number}&gt;} played Contacts who have listened to the voice message
     * @property {number} playedRemaining Amount of people who have not listened to the message
     * @property {Array&amp;lt;{id: ContactId, t: number}&gt;} read Contacts who have read the message
     * @property {number} readRemaining Amount of people who have not read the message
     */

    /**
     * Get information about message delivery status.
     * May return null if the message does not exist or is not sent by you.
     * @returns {Promise&amp;lt;?MessageInfo&gt;}
     */
    async getInfo() {
        const info &#x3D; await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            if (!msg || !msg.id.fromMe) return null;

            return new Promise((resolve) &#x3D;&gt; {
                setTimeout(async () &#x3D;&gt; {
                    resolve(await window.Store.getMsgInfo(msg.id));
                }, (Date.now() - msg.t * 1000 &amp;lt; 1250) &amp;amp;&amp;amp; Math.floor(Math.random() * (1200 - 1100 + 1)) + 1100 || 0);
            });
        }, this.id._serialized);

        return info;
    }

    /**
     * Gets the order associated with a given message
     * @return {Promise&amp;lt;Order&gt;}
     */
    async getOrder() {
        if (this.type &#x3D;&#x3D;&#x3D; MessageTypes.ORDER) {
            const result &#x3D; await this.client.pupPage.evaluate((orderId, token, chatId) &#x3D;&gt; {
                return window.WWebJS.getOrderDetail(orderId, token, chatId);
            }, this.orderId, this.token, this._getChatId());
            if (!result) return undefined;
            return new Order(this.client, result);
        }
        return undefined;
    }
    /**
     * Gets the payment details associated with a given message
     * @return {Promise&amp;lt;Payment&gt;}
     */
    async getPayment() {
        if (this.type &#x3D;&#x3D;&#x3D; MessageTypes.PAYMENT) {
            const msg &#x3D; await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
                const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
                if(!msg) return null;
                return msg.serialize();
            }, this.id._serialized);
            return new Payment(this.client, msg);
        }
        return undefined;
    }


    /**
     * Reaction List
     * @typedef {Object} ReactionList
     * @property {string} id Original emoji
     * @property {string} aggregateEmoji aggregate emoji
     * @property {boolean} hasReactionByMe Flag who sent the reaction
     * @property {Array&amp;lt;Reaction&gt;} senders Reaction senders, to this message
     */

    /**
     * Gets the reactions associated with the given message
     * @return {Promise&amp;lt;ReactionList[]&gt;}
     */
    async getReactions() {
        if (!this.hasReaction) {
            return undefined;
        }

        const reactions &#x3D; await this.client.pupPage.evaluate(async (msgId) &#x3D;&gt; {
            const msgReactions &#x3D; await window.Store.Reactions.find(msgId);
            if (!msgReactions || !msgReactions.reactions.length) return null;
            return msgReactions.reactions.serialize();
        }, this.id._serialized);

        if (!reactions) {
            return undefined;
        }

        return reactions.map(reaction &#x3D;&gt; {
            reaction.senders &#x3D; reaction.senders.map(sender &#x3D;&gt; {
                sender.timestamp &#x3D; Math.round(sender.timestamp / 1000);
                return new Reaction(this.client, sender);
            });
            return reaction;
        });
    }

    /**
     * Edits the current message.
     * @param {string} content
     * @param {MessageEditOptions} [options] - Options used when editing the message
     * @returns {Promise&amp;lt;?Message&gt;}
     */
    async edit(content, options &#x3D; {}) {
        if (options.mentions) {
            !Array.isArray(options.mentions) &amp;amp;&amp;amp; (options.mentions &#x3D; [options.mentions]);
            if (options.mentions.some((possiblyContact) &#x3D;&gt; possiblyContact instanceof Contact)) {
                console.warn(&#x27;Mentions with an array of Contact are now deprecated. See more at https://github.com/pedroslopez/whatsapp-web.js/pull/2166.&#x27;);
                options.mentions &#x3D; options.mentions.map((a) &#x3D;&gt; a.id._serialized);
            }
        }

        options.groupMentions &amp;amp;&amp;amp; !Array.isArray(options.groupMentions) &amp;amp;&amp;amp; (options.groupMentions &#x3D; [options.groupMentions]);

        let internalOptions &#x3D; {
            linkPreview: options.linkPreview &#x3D;&#x3D;&#x3D; false ? undefined : true,
            mentionedJidList: options.mentions || [],
            groupMentions: options.groupMentions,
            extraOptions: options.extra
        };
        
        if (!this.fromMe) {
            return null;
        }
        const messageEdit &#x3D; await this.client.pupPage.evaluate(async (msgId, message, options) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            if (!msg) return null;

            let canEdit &#x3D; window.Store.MsgActionChecks.canEditText(msg) || window.Store.MsgActionChecks.canEditCaption(msg);
            if (canEdit) {
                const msgEdit &#x3D; await window.WWebJS.editMessage(msg, message, options);
                return msgEdit.serialize();
            }
            return null;
        }, this.id._serialized, content, internalOptions);
        if (messageEdit) {
            return new Message(this.client, messageEdit);
        }
        return null;
    }

    /**
     * Edits the current ScheduledEvent message.
     * Once the scheduled event is canceled, it can not be edited.
     * @param {ScheduledEvent} editedEventObject
     * @returns {Promise&amp;lt;?Message&gt;}
     */
    async editScheduledEvent(editedEventObject) {
        if (!this.fromMe) {
            return null;
        }

        const edittedEventMsg &#x3D; await this.client.pupPage.evaluate(async (msgId, editedEventObject) &#x3D;&gt; {
            const msg &#x3D; window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
            if (!msg) return null;

            const { name, startTimeTs, eventSendOptions } &#x3D; editedEventObject;
            const eventOptions &#x3D; {
                name: name,
                description: eventSendOptions.description,
                startTime: startTimeTs,
                endTime: eventSendOptions.endTimeTs,
                location: eventSendOptions.location,
                callType: eventSendOptions.callType,
                isEventCanceled: eventSendOptions.isEventCanceled,
            };

            await window.Store.ScheduledEventMsgUtils.sendEventEditMessage(eventOptions, msg);
            const editedMsg &#x3D; window.Store.Msg.get(msg.id._serialized);
            return editedMsg?.serialize();
        }, this.id._serialized, editedEventObject);

        return edittedEventMsg &amp;amp;&amp;amp; new Message(this.client, edittedEventMsg);
    }
    /**
     * Returns the PollVote this poll message
     * @returns {Promise&amp;lt;PollVote[]&gt;}
     */
    async getPollVotes() {
        return await this.client.getPollVotes(this.id._serialized);
    }
}

module.exports &#x3D; Message;
</code></pre>
          </article>
        </div>
      </div>
      <nav id="jsdoc-toc-nav" role="navigation"></nav>
    </div>
  </div>
  <footer id="jsdoc-footer" class="jsdoc-footer">
    <div id="jsdoc-footer-container">
      <p>
        Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc</a> 3.6.11 on November 6, 2025.
      </p>
    </div>
  </footer>
  <script src="scripts/jquery.min.js"></script>
  <script src="scripts/tree.jquery.js"></script>
  <script src="scripts/prettify.js"></script>
  <script src="scripts/jsdoc-toc.js"></script>
  <script src="scripts/linenumber.js"></script>
  <script src="scripts/scrollanchor.js"></script>
</body>

</html>